﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Runtime.Serialization;
using System.Diagnostics;
using System.IO.IsolatedStorage;
using System.IO;
using Curling.CurlingServer;

namespace Curling
{
    [DataContractAttribute]
    public class StoneEntity
    {
        private Stone stone_;
        public Stone stone
        {
            get { return stone_; }
            set
            {
                stone_ = value;
                stone.Out.Completed += new EventHandler(Out_Completed);
                width = stone.Width;
                height = stone.Height;
                width_2 = width / 2.0;
                height_2 = height / 2.0;

                Initialize();
            }
        }

        public const double Radius = 28.0;

        public static double FrictionWithPlayer = 90;
        public static double FrictionWithoutPlayer = 20;
        public static double FrictionWithBroom = 10;
        public static double AngularFrictionWithoutBroom = 1;
        public static double AngularFrictionWithBroom = 0.5;

        [DataMemberAttribute]
        public double Friction = FrictionWithoutPlayer;
        [DataMemberAttribute]
        public double AngularFriction = AngularFrictionWithoutBroom;
        [DataMemberAttribute]
        public double Curl = 10.0;
        [DataMemberAttribute]
        public double DistanceToButton = double.MaxValue;

        [DataMemberAttribute]
        public StoneColor stoneColor { get; set; }

        [DataMemberAttribute]
        public SoundManager soundManager { get; set; }

        [DataMemberAttribute]
        public int ID { get; set; }

        [DataMemberAttribute]
        public double x;
        public double X
        {
            get
            {
                return GameManager.ConvertXBack(x);
            }
            set
            {
                x = GameManager.ConvertX(value);
                stone.Margin = new Thickness(x - width_2, y - height_2, 0, 0);
            }
        }        

        [DataMemberAttribute]
        public double y;
        public double Y
        {
            get
            {
                return GameManager.ConvertYBack(y);
            }
            set
            {
                y = GameManager.ConvertY(value);
                stone.Margin = new Thickness(x - width_2, y - height_2, 0, 0);
            }
        }        

        [DataMemberAttribute]
        public double rotation;
        public double Rotation
        {
            get
            {
                return rotation;
            }
            set
            {
                rotation = value;
                stone.stone.RenderTransform = new RotateTransform() { Angle = rotation, CenterX = width_2, CenterY = height_2 };
            }
        }
        
        [DataMemberAttribute]
        public double VelocityX { get; set; }
        [DataMemberAttribute]
        public double VelocityY { get; set; }
        [DataMemberAttribute]
        public double AngularVelocity { get; set; }

        [DataMemberAttribute]
        public bool IsStatic = true;
        [DataMemberAttribute]
        public bool isProceed;
        public bool IsProceed
        {
            get { return isProceed; }
            set
            {
                bool LastProceedState = isProceed;
                isProceed = value;

                if (isProceed && !LastProceedState)
                {
                    Save();
                }
            }
        }

        [DataMemberAttribute]
        public StoneState state;
        public StoneState State
        {
            get { return state; }
            set
            {
                if (stone != null)
                {
                    StoneState LastState = state;
                    state = value;

                    if (state != StoneState.InOut)
                    {
                        stone.LayoutRoot.Opacity = 1.0;
                    }
                    else
                    {
                        if (LastState == StoneState.InOut)
                        {
                            stone.Out.Stop();
                            stone.LayoutRoot.Opacity = 0.0;
                            IsProceed = true;
                        }
                        
                        if (stone.LayoutRoot.Opacity == 1.0)//LastState != StoneState.InOut)
                        {
                            stone.Out.Begin();
                        }
                        DistanceToButton = double.MaxValue;
                    }

                    if (state == StoneState.WaitDelivery && LastState != StoneState.WaitDelivery)
                    {
                        Save();
                    }
                }
            }
        }

        [DataMemberAttribute]
        public Fall Fall { get; set; }

        public double width;
        public double height;
        public double width_2;
        public double height_2;

        public StoneEntity()
        {
            soundManager = new SoundManager();
        }

        public void Initialize()
        {
            X = X;
            Y = Y;
            Rotation = Rotation;
            State = State;
        }

        public void Save()
        {
            //if (!IsStatic)
           // {
                using (IsolatedStorageFile Store = IsolatedStorageFile.GetUserStoreForApplication())
                {
                    if (!Store.DirectoryExists("Stones"))
                    {
                        Store.CreateDirectory("Stones");
                    }

                    string Path = "\\Stones\\" + ID + ".xml";

                    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(Path, FileMode.Create, Store))
                    {
                        DataContractSerializer dtaContractSerializer = new DataContractSerializer(typeof(StoneEntity));
                        dtaContractSerializer.WriteObject(stream, this);
                    }
                }
            //}
        }

        public static StoneEntity Load(int ID)
        {
            using (IsolatedStorageFile Store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!Store.DirectoryExists("Stones"))
                {
                    Store.CreateDirectory("Stones");
                }

                string Path = "\\Stones\\" + ID + ".xml";

                using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(Path, FileMode.OpenOrCreate, Store))
                {
                    if (stream.Length > 0)
                    {
                        DataContractSerializer dataContractSerializer = new DataContractSerializer(typeof(StoneEntity));
                        return dataContractSerializer.ReadObject(stream) as StoneEntity;
                    }
                    return null;
                }
            }
        }

        public StoneEntity(Stone stone)
        {
            this.stone = stone;

            //State = StoneState.WaitDelivery;

            soundManager = new SoundManager();

            stone.Out.Completed += new EventHandler(Out_Completed);
        }

        public void Update(double dT)
        {
            UpdatePosition(VelocityX, VelocityY, AngularVelocity, dT);
            UpdateVelocity(Friction, AngularFriction, dT);
        }

        private void UpdatePosition(double VelocityX, double VelocityY, double AngularVelocity, double dT)
        {
            if (Math.Abs(VelocityX) > 0.0 || Math.Abs(VelocityY) > 0.0 || Math.Abs(AngularVelocity) > 0.0)
            {
                X += VelocityX * dT;
                Y += VelocityY * dT;
                Rotation += AngularVelocity * dT;

                IsStatic = false;
            }
            else
            {
                if (!IsStatic)
                {
                    DistanceToButton = Math.Sqrt(Math.Pow(X - GameManager.Centerline, 2) + Math.Pow(Y - (GameManager.RinkLength - GameManager.Teeline), 2));
                    IsStatic = true;
                }
            }
        }

        private void UpdateVelocity(double Friction, double AngularFriction, double dT)
        {
            if (!IsStatic)
            {
                double Velocity = 0.0;                

                if (Math.Abs(VelocityX) > 0.0 || Math.Abs(VelocityY) > 0.0)
                {
                    Velocity = Math.Sqrt(VelocityX * VelocityX + VelocityY * VelocityY);
                    
                    double Va = AngularVelocity * 0.0000005 * Curl * (1 / (Velocity / 20 + 1) + 1);

                    double Vx = VelocityX * Math.Cos(-Va) - VelocityY * Math.Sin(-Va);
                    double Vy = VelocityY * Math.Cos(Va) + VelocityX * Math.Sin(Va);

                    VelocityX = Vx;
                    VelocityY = Vy;   

                    PlayMoveSound(Velocity);

                    double NewVelocity = Math.Max(Velocity - Friction * dT, 0.0);

                    double d = NewVelocity / Velocity;

                    VelocityX *= d;
                    VelocityY *= d;
                }

                double NewAngularVelocity = AngularVelocity - Math.Sign(AngularVelocity) * AngularFriction * Math.Min((20 / Velocity + 1), 40) * dT;

                AngularVelocity = (Math.Sign(AngularVelocity) == Math.Sign(NewAngularVelocity)) ? NewAngularVelocity : 0.0;
            }
        }

        public void CheckPosition(GameState gameState)
        {
            Fall = null;

            if (State != StoneState.InOut)
            {
                if (gameState == GameState.StonePushed)
                {
                    if (Y + StoneEntity.Radius > GameManager.Hogline)
                    {
                        Fall = new Fall() { type = FallType.FirstHogline };
                        return;
                    }
                }

                if (IsStatic && gameState == GameState.StoneDelivered && !IsProceed)
                {
                    if (Y + StoneEntity.Radius < GameManager.RinkLength - GameManager.Hogline)
                    {
                        Fall = new Fall() { type = FallType.SecondHogline };
                        return;
                    }
                    else if (Y - StoneEntity.Radius > GameManager.RinkLength - GameManager.Backline)
                    {
                        Fall = new Fall() { type = FallType.Backline };
                        return;
                    }
                }

                if (!IsStatic)
                {
                    if ((X - StoneEntity.Radius < 0.0 || X + StoneEntity.Radius > GameManager.RinkWidth))
                    {
                        VelocityX = 0.0;
                        VelocityY = 0.0;
                        AngularVelocity = 0.0;
                        Fall = new Fall() { type = FallType.Out };
                        soundManager.PlayMoveSound(0.0);
                        return;
                    }

                    if (Y + StoneEntity.Radius > GameManager.RinkLength)
                    {
                        VelocityX = 0.0;
                        VelocityY = 0.0;
                        AngularVelocity = 0.0;
                        Fall = new Fall() { type = FallType.BackOut };
                        soundManager.PlayMoveSound(0.0);
                        return;
                    }
                }

                if (IsStatic && !IsProceed && gameState == GameState.StoneDelivered)
                {
                    IsProceed = true;
                }
            }

            return;
        }

        public double CheckCollision(StoneEntity stone)
        {
            if (State != StoneState.InOut && stone.State != StoneState.InOut)
            {
                if (X + Radius >= stone.X - Radius && X - Radius <= stone.X + Radius && Y + Radius >= stone.Y - Radius && Y - Radius <= stone.Y + Radius)
                {
                    double DistanceSquare = (X - stone.X) * (X - stone.X) + (Y - stone.Y) * (Y - stone.Y);

                    if (DistanceSquare <= 4 * Radius * Radius)
                    {
                        IsProceed = false;
                        IsStatic = false;
                        stone.IsProceed = false;
                        stone.IsStatic = false;
                        return ProcessCollision(this, stone);
                    }
                }
            }

            return 0;
        }

        private static double ProcessCollision(StoneEntity stone1, StoneEntity stone2)
        {
            double x_axis_X = stone2.X - stone1.X;
            double x_axis_Y = stone2.Y - stone1.Y;
            double len = Math.Sqrt(x_axis_X * x_axis_X + x_axis_Y * x_axis_Y);
            x_axis_X /= len;
            x_axis_Y /= len;
            double mass = 1.0;
            double p = (x_axis_X * stone2.VelocityX + x_axis_Y * stone2.VelocityY) * mass - (x_axis_X * stone1.VelocityX + x_axis_Y * stone1.VelocityY) * mass;
            if (p < 0)
            {
                double dpx = x_axis_X * p;
                double dpy = x_axis_Y * p;
                stone1.VelocityX += dpx;
                stone1.VelocityY += dpy;

                stone2.VelocityX -= dpx;
                stone2.VelocityY -= dpy;

                stone1.AngularVelocity -= 0.5 * p * (x_axis_Y - x_axis_X);
                stone2.AngularVelocity += 0.5 * p * (x_axis_Y - x_axis_X);

                return -p;
            }

            return 0;
        }

        public void PlayMoveSound(double Velocity)
        {
            soundManager.PlayMoveSound(Math.Min(Velocity / 400.0, 1.0));
        }

        //public void StoneOut()
        //{
        //    if (State != StoneState.InOut)
        //    {
        //        stone.Out.Begin();
        //    }
        //}

        void Out_Completed(object sender, EventArgs e)
        {
            if (!IsProceed)
                IsProceed = true;
        }

        public void AddRotationImpulse(double RotationImpulse, double dT)
        {
            AngularVelocity += RotationImpulse * dT;
            AngularVelocity = Math.Max(Math.Min(AngularVelocity, 200), -200);
        }
    }
}
